/*
 * simple_table.c
 *
 *  Created on: 2012-3-4
 *      Author: lin
 */
#include <assert.h>
#include <string.h>
#include "simple_table.h"
#include "zmalloc/zmalloc.h"

#define DEF_RATE  8

static inline int _simple_table_index(const simple_table_t* table,
        const void* key) {
    void** keys = table->keys;
    for (int i = 0; i < table->max_num; i++) {
        void* ikey = keys[i];
        if (ikey && !table->key_cmp(key, ikey)) {
            return i;
        }
    }
    return -1;
}

static void _simple_table_expand_if_need(simple_table_t* table) {
    int max_num = table->max_num;
    int new_max_num;
    if (table->num == max_num) {
        new_max_num = table->rate + max_num;
        void** keys = zcalloc(new_max_num * sizeof(void*));
        void** values = zcalloc(new_max_num * sizeof(void*));
        memcpy(keys, table->keys, max_num * sizeof(void*));
        memcpy(values, table->values, max_num * sizeof(void*));
        zfree(table->keys);
        zfree(table->values);
        table->keys = keys;
        table->values = values;
        table->max_num = new_max_num;
    }
}

static inline void _simple_table_insert(simple_table_t* table, const void* key,
        const void* value) {
    for (int i = 0; i < table->max_num; i++) {
        if (!table->keys[i]) {
            table->keys[i] = (void*) key;
            table->values[i] = (void*) value;
            ++table->num;
            return;
        }
    }
    assert(0);
}
void simple_table_kv_free(void *key, void *val) {
    zfree(key);
    zfree(val);
}

int simple_table_char_key_cmp(const char *l, const char *r) {
    return strcmp(l, r);
}

simple_table_t* simple_table_new0(uint32_t max_num,
        simple_table_key_cmp_proc cmp, simple_table_kv_free_proc kv_del,
        uint32_t rate) {
    assert(cmp);

    void** keys = zcalloc(max_num * sizeof(void*));
    void** values = zcalloc(max_num * sizeof(void*));
    simple_table_t* table = zmalloc(sizeof(simple_table_t));
    table->keys = keys;
    table->values = values;
    table->num = 0;
    table->max_num = max_num;
    table->key_cmp = cmp;
    table->kv_del = kv_del;
    table->rate = rate;
    return table;
}
simple_table_t* simple_table_new1(uint32_t max_num,
        simple_table_key_cmp_proc cmp, simple_table_kv_free_proc kv_del) {
    return simple_table_new0(max_num, cmp, simple_table_kv_free, max_num);
}

void simple_table_free(simple_table_t *table) {
    if (table->kv_del) {
        for (int i = 0; i < table->max_num; i++) {
            if (table->keys[i]) {
                table->kv_del(table->keys[i], table->values[i]);
                table->keys[i] = NULL;
                table->values[i] = NULL;
            }
        }
    }
    zfree(table->keys);
    zfree(table->values);
    zfree(table);
}

int simple_table_put(simple_table_t* table, const void* key, const void* value) {
    int idx = _simple_table_index(table, key);
    if (idx != -1) {
        return 0;
    }
    _simple_table_expand_if_need(table);
    _simple_table_insert(table, key, value);
    return 1;
}

void* simple_table_get(const simple_table_t* table, const void* key) {
    int idx = _simple_table_index(table, key);
    if (idx != -1) {
        return (void*) table->values[idx];
    }
    return NULL;
}

int simple_table_remove(simple_table_t* table, const void* key,
        const void** oldkey, const void** oldvalue) {
    int idx = _simple_table_index(table, key);
    if (idx == -1) {
        return 0;
    }

    *oldvalue = table->values[idx];
    *oldkey = table->keys[idx];
    table->values[idx] = NULL;
    table->keys[idx] = NULL;
    return 1;
}

int simple_table_exist(const simple_table_t* table, const void* key) {
    return _simple_table_index(table, key) != -1;
}

simple_table_iter_t* simple_table_iter_get(const simple_table_t *table,
        int reverse) {
    simple_table_iter_t *iter = zmalloc(sizeof(simple_table_iter_t));
    iter->reverse = reverse;
    iter->table = table;
    iter->index = reverse ? table->max_num : -1;
    return iter;
}

void simple_table_rewind(const simple_table_t *table, simple_table_iter_t *iter,
        int reverse) {
    iter->reverse = reverse;
    iter->table = table;
    iter->index = reverse ? table->max_num : -1;
}

int simple_table_iter_next(simple_table_iter_t *iter, void **key, void **value) {
    const simple_table_t *table = iter->table;
    if (iter->reverse) {
        while (--iter->index >= 0) {
            if (table->keys[iter->index]) {
                *key = table->keys[iter->index];
                *value = table->values[iter->index];
                return 1;
            }
        }
    } else {
        while (++iter->index < table->max_num) {
            if (table->keys[iter->index]) {
                *key = table->keys[iter->index];
                *value = table->values[iter->index];
                return 1;
            }
        }
    }

    return 0;
}

void simple_table_iter_free(simple_table_iter_t *iter) {
    zfree(iter);
}
